﻿using System;
using System.Collections.Generic;

namespace ZeBobo5.LibraryHelper.Orm
{
    public class OrmField<A, T> : OrmFieldBase<A>
        where A : OrmDataElementBase<A>
    {
        #region Delegates

        public delegate void FieldEventHandler(OrmField<A, T> sender, OrmValues<T> value);

        public delegate void FieldValidateErrorEventHandler(OrmField<A, T> sender, OrmValues<T> value, object valueToSet, Exception ex);

        public delegate void Validation(T a);

        #endregion

        private readonly T _dft;
        private readonly List<Validation> _validators = new List<Validation>();

        public OrmField(string name, T defaultValue)
        {
            FieldName = name;
            _dft = defaultValue;
        }

        public OrmField(string name, T defaultValue, params Validation[] validators) :
            this(name, defaultValue)
        {
            _validators.AddRange(validators);
        }

        public List<Validation> Validators
        {
            get
            {
                return _validators;
            }
        }

        public OrmValues<T> Values(OrmDataElementBase<A> el)
        {
            if (!_ormValues.ContainsKey(el))
            {
                OrmValues<T> val = new OrmValues<T>();
                val.OriginalValue = _dft;
                val.CurrentValue = _dft;
                val.FieldChanging +=
                    delegate
                        {
                            OnFieldChanging(val);
                            el.OnFieldChanging(this);
                        };
                val.FieldValidating +=
                    delegate
                        (T valueToSet)
                        {
                            //OnFieldValidating(val);
                            try
                            {
                                foreach (Validation validator in _validators)
                                {
                                    validator(valueToSet);
                                }
                            }
                            catch (Exception ex)
                            {
                                Exception e = new Exception("Error on validating field.", ex);
                                OnFieldValidateError(val, valueToSet, e);
                            }
                        };
                val.FieldValidated +=
                    delegate
                        {
                            OnFieldValidated(val);
                        };
                val.FieldChanged +=
                    delegate
                        {
                            OnFieldChanged(val);
                            el.OnFieldChanged(this);
                        };
                _ormValues[el] = val;
            }
            el.AddOwnerField<T>(this);
            return (OrmValues<T>) _ormValues[el];
        }

        public override Type ValuesType()
        {
            return typeof (T);
        }

        public void OnFieldChanging(OrmValues<T> value)
        {
            if (FieldChanging != null)
            {
                FieldChanging(this, value);
            }
        }

        public void OnFieldChanged(OrmValues<T> value)
        {
            if (FieldChanged != null)
            {
                FieldChanged(this, value);
            }
        }

        public void OnFieldValidating(OrmValues<T> value)
        {
            if (FieldValidating != null)
            {
                FieldValidating(this, value);
            }
        }

        public void OnFieldValidated(OrmValues<T> value)
        {
            if (FieldValidated != null)
            {
                FieldValidated(this, value);
            }
        }

        public void OnFieldValidateError(OrmValues<T> value, object valueToSet, Exception ex)
        {
            if (FieldValidateError != null)
            {
                FieldValidateError(this, value, valueToSet, ex);
            }
            else
            {
                throw ex;
            }
        }

        public event FieldEventHandler FieldChanging;
        public event FieldEventHandler FieldChanged;

        public event FieldEventHandler FieldValidating;
        public event FieldEventHandler FieldValidated;
        public event FieldValidateErrorEventHandler FieldValidateError;
    }
}